/* 
  1、promise是一个类，参数是一个执行器函数executor，这个执行器函数具有两个回调函数resolve和reject。
  2、resolve和reject， 均是用来更改promise状态的。resolve具有一个参数可以接收成功返回的信息， reject具有一个参数可以接收失败信息
  3、promise具有三种状态，pending 等待、fulfilled 成功、rejected 失败，一旦状态变更为成功或者失败，就不能再次变更。
  resolve == 》用于将状态从pending 改变为 fulfilled 成功
  reject == 》用于将状态从pending 改变为 rejected 失败
  4、promise具有一个 then方法 ，是用来判断状态的。
  如果状态是成功，则调用成功回调函数，如果状态是失败，则调用失败回调函数。
  该方法是挂载在原型对象上的，then方法具有两个参数，第一个参数是成功的回调函数，第二个参数是失败的回调函数；
  成功/失败的回调函数均具有一个参数，用于接收resolve/reject中的成功之后的值value/失败的原因reason。
  5、异步编程：为了满足promise执行器中具有 异步编程 的需求，then函数中需要把成功、失败的回调函数临时存储起来；
    由于同一个promise实例有可能多次调用then方法，此时如果执行器中有异步编程，那么存储成功/失败的回调函数的变量必须是数组；
    等到可以执行resolve、 reject函数时再将数组中的函数一一执行
  6、 为了满足promise的then方法 链式编程 。
      1） then方法中需要返回一个新的promise对象， 这里一定不能返回当前的promise对象， 否则将会进入死循环报错；
      2） 后面then方法的回调函数拿到的值是上一个then方法的回调函数的值：
          - 当这个值为普通值时，直接通过resolve(value)传递即可；
          - 当这个值为promise对象时，需要确定promise对象的状态后，再确定是通过resolve/reject传递值。
      3） 链式编程如果then方法调用时没有参数，则成功函数自己会内部将上一个value值返回，即value=>value；失败函数会将错误原因抛出，即reason=>throw reason;
  7、错误处理：需要捕获执行器函数executor内部的错误、需要捕获then函数内部的错误、需要在catch函数中继续传递错误
  8、 promise具有一个catch函数， 绑定在原型对象上的， 在catch函数内执行一个then方法即可， 第一个成功回调传递undefined
  9、 promise.all promise的静态方法，解决异步按序并发的问题，接收一个参数是 由多个promise对象/普通值 组成的数组，成功回调的参数是个数组。
      1）只有数组内的所有的promise对象均状态成功，才能够在then方法中进入成功回调；
      2）只要有一个promise对象状态为失败，就进入失败回调
      3) 参数数组中，某一项是普通值时，直接返回普通值；如果是promise对象，则返回promise对象的结果。
  10、promise.resolve promise的静态方法，接收一个参数，这个参数可以是普通值，可以是promise对象
       1）如果是普通值，则使用promise实例化一个promise对象返回
       2）如果是promise对象，则直接返回promise对象
  11、finally 接收一个参数是函数，无论上一个promise结果是成功or失败，均会执行一次这个回调函数。
  */

// promise的三种状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

// promise是一个类
class MyPromise {
    constructor(executor) {
        // 捕捉错误：错误均使用reject函数抛出。
        try {
            // 具有一个立即执行的执行器函数，两个参数分别用来改变状态
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error);
        }
    }
    status = PENDING
        // 用于在改变状态的resolve/reject方法中存储成功/失败的值，方便在then方法的回调函数中使用
    value = undefined
    reason = undefined
        // 同一个promise可以多次执行then函数，就有可能会有多个回调函数需要存储，所以使用数组
    successCallback = []
    failCallback = []
        // 将状态变为成功 fulfilled
    resolve = (value) => {
            if (this.status !== PENDING) return
            this.value = value;
            this.status = FULFILLED;
            // 异步编程：当promise执行器函数中具有异步编程时，需要判断是否有成功回调，如果有就依次执行
            // 再then函数中经过处理后的函数已经能够获取value值了，所以调用时不用传参
            while (this.successCallback.length) this.successCallback.shift()();
        }
        // 将状态变为失败 rejected
    reject = (reason) => {
            if (this.status !== PENDING) return
            this.reason = reason;
            this.status = REJECTED;
            // 异步编程：当promise执行器函数中具有异步编程时，需要判断是否有失败回调，如果有就依次执行
            // 再then函数中经过处理后的函数已经能够获取value值了，所以调用时不用传参
            while (this.failCallback.length) this.failCallback.shift()();
        }
        // 判断状态
    then(successCallback, failCallback) {
        // 无回调函数时：当then方法没有回调参数时，自己内部需要自己创建一个函数进行值的传递
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => {
            throw reason
        };

        // 链式编程：为了满足链式编程，需要返回一个新的promise对象
        let promise2 = new MyPromise((resolve, reject) => {
            // 由于执行器函数是立即执行的，所以将判断状态的代码放在执行器内部即可
            if (this.status === FULFILLED) {
                // 状态：成功
                // 为了进入异步消息队列
                setTimeout(() => {
                    try {
                        // 链式编程传递返回值：将 这次 回调函数 的返回值 传递给 下一个then方法的回调函数（resove/reject） 中去
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                // 状态：失败
                // 为了进入异步消息队列
                setTimeout(() => {
                    try {
                        // 链式编程传递返回值：将 这次 回调函数 的返回值 传递给 下一个then方法的回调函数（resove/reject） 中去
                        let x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }

                }, 0);
            } else {
                // 状态：等待
                // 异步编程：当promise执行器函数中具有异步编程时，此时状态为pending，为了异步处理时能够正常执行对应的成功/失败回调，需要存储回调函数
                // 处理回调函数：链式编程传递值 && 捕获错误，为了能够处理函数，这里向数组里存储一个方法。
                // this.successCallback.push(successCallback)
                // this.failCallback.push(failCallback)
                this.successCallback.push(() => {
                    // 为了进入异步消息队列
                    setTimeout(() => {
                        try {
                            // 链式编程传递返回值：将 这次 回调函数 的返回值 传递给 下一个then方法的回调函数（resove/reject） 中去
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                })
                this.failCallback.push(() => {
                    // 为了进入异步消息队列
                    setTimeout(() => {
                        try {
                            // 链式编程传递返回值：将 这次 回调函数 的返回值 传递给 下一个then方法的回调函数（resove/reject） 中去
                            let x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }

                    }, 0);
                })
            }
        })

        return promise2;
    }
    catch (failCallback) {
        // 这里需要return一个promise对象实现链式编程
        return this.then(undefined, failCallback)
    }
    static all(array) {
        let result = [];
        // index是为了解决异步编程时，避免出现某一个promise还未执行完毕，resolve已经改变了状态的问题
        let index = 0;
        return new MyPromise((resolve, reject) => {
            function addData(i, value) {
                result[i] = value;
                index++;
                // 每一次添加数据时，判断一下是否已经执行完毕
                if (index == array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    // promise对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, current)
                }
            }
        })
    }
    static resolve(value) {
        // promise对象直接返回
        if (value instanceof MyPromise) return value;
        // 普通值需要包装一下resolve返回
        return new MyPromise((resolve) => resolve(value))
    }
    finally(callback) {
        return this.then(
            value => MyPromise.resolve(callback()).then(() => value),
            reason => MyPromise.resolve(callback()).then(() => {
                throw reason
            })
        )
    }
}
/**
 * 解析then函数中回调函数的返回值：
 * 回调函数返回值类型不同， 处理不同。
 * @param {*} promise2 同一个promise对象
 * @param {*} x then方法中回调函数的返回值
 * @param {*} resolve promise对象的执行器中的成功回调函数
 * @param {*} reject promise对象的执行器中的失败回调函数
 * @returns
 */
function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        // 不能返回同一个promise对象， 否则将会进入死循环，丢出个错误。
        return new TypeError('你返回了同一个promise对象，进入死循环啦！')
    }
    if (x instanceof MyPromise) {
        //promise对象：需要确定promise状态后，再确定使用resolve/reject传递值
        x.then(resolve, reject)
    } else {
        //普通值：直接resolve传递
        resolve(x);
    }
}

module.exports = MyPromise;